/*
 * @Author: tanjunyi@doctorwork.com
 * @Date: 2020-06-21 11:41:42
 * @LastEditors: tanjunyi@doctorwork.com
 * @LastEditTime: 2020-06-26 22:27:53
 * @Description:
 */

/**
 * 节点diff算法，渲染
 * @param {*} oldVnode 
 * @param {*} newVnode 
 */
export function patch(oldVnode, newVnode) {
	const isRealElement = oldVnode.nodeType;
	let el = createElm(newVnode);

	if (isRealElement) {
		// 真是元素
		const oldElm = oldVnode;
		const parentElm = oldElm.parentNode;
		// 在原始节点下面插入新节点，然后删除原始节点
		parentElm.insertBefore(el, oldElm.nextSibling)
		parentElm.removeChild(oldElm);
		return el; //渲染的真是节点
	} else {
		// DOM diff算法，同层比较
		if (oldVnode.tag !== newVnode.tag) {	//标签名不一致
			oldVnode.el.parentNode.replaceChild(createElm(newVnode), oldVnode.el)
		}
		// 如果是文本
		if (!oldVnode.tag) {
			if (oldVnode.text !== newVnode.text) {
				oldVnode.el.textContent = newVnode.text;
			}
		}
		// 标签一致
		// 复用老节点，更新属性
		let el = newVnode.el = oldVnode.el;
		updateProperties(newVnode, oldVnode.data);
		//---- 子节点diff -----
		let oldChildren = oldVnode.children || [];
		let newChildren = newVnode.children || [];
		// 1, 节点插入
		// 2，节点删除
		// 3，子节点对比
		if (oldChildren.length && newChildren.length) {
			updateChildren(el, oldChildren, newChildren);
		} else if (oldChildren.length) {
			el.innelHTML = ''
		} else if (newChildren.length) {
			newChildren.forEach(child => {
				el.appendChild(createElm(child));
			})
		}
		return el;
	}
}

function isSameVnode(oldVnode, newVnode) {
	return (oldVnode.key === newVnode.key) && (oldVnode.tag === newVnode.tag)
}
/**
 * 子节点diff算法比对
 * @param {*} el 
 * @param {*} oldChildren 
 * @param {*} newChildren 
 */
function updateChildren(parent, oldChildren, newChildren) {
	// Vue2.0 使用双指针 来进行比较
	let oldStartIndex = 0; //老的开始元素索引
	let oldStartVnode = oldChildren[0]; //老的开始元素
	let oldEndIndex = oldChildren.length - 1; // 老的结束索引
	let oldEndVnode = oldChildren[oldEndIndex]; //老的结束元素

	let newStartIndex = 0; //新的开始元素索引
	let newStartVnode = newChildren[0]; //新的开始元素
	let newEndIndex = newChildren.length - 1; // 新的结束索引
	let newEndVnode = newChildren[newEndIndex]; //新的结束元素
	/**
	 * 数组转换成对象，解决乱序
	 * @param {*} children 
	 */
	function makeIndexByKey(children) {
		let map = {};
		children.forEach((item, index) => {
			map[item.key] = index;
			return map;
		})
		return map;
	}
	let map = makeIndexByKey(oldChildren);
	// 1. 从头部开始比较
	while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
		// 如果元素为null 
		if (!oldStartVnode) {
			oldStartVnode = oldChildren[++oldStartIndex];
		} else if (!oldEndVnode) {
			oldEndVnode = oldChildren[--oldEndIndex]
		}

		// 如何判断两个虚拟标签是否一致，看key和type
		else if (isSameVnode(oldStartVnode, newStartVnode)) {
			// 从前到后
			// 标签和key一致，但是元素属性可能不一致
			patch(oldStartVnode, newStartVnode);
			oldStartVnode = oldChildren[++oldStartIndex];
			newStartVnode = newChildren[++newStartIndex];
		} else if (isSameVnode(oldEndVnode, newEndVnode)) {
			// 从后到前
			patch(oldEndVnode, newEndVnode);
			oldEndVnode = oldChildren[--oldEndIndex];
			newEndVnode = newChildren[--newEndIndex];
		} else if (isSameVnode(oldStartVnode, newEndVnode)) {
			// reverse转换顺序
			patch(oldStartVnode, newEndVnode);
			parent.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling); //注意具备移动性
			oldStartVnode = oldChildren[++oldStartIndex];
			newEndVnode = newChildren[--newEndIndex];
		} else if (isSameVnode(oldEndVnode, newStartVnode)) {
			// reverse转换顺序
			patch(oldEndVnode, newStartVnode);
			parent.insertBefore(oldEndVnode.el, oldStartVnode.el); //注意具备移动性
			oldEndVnode = oldChildren[--oldEndIndex];
			newStartVnode = newChildren[++newStartIndex];
		} else {
			// 乱序比对
			let moveIndex = map[newStartVnode.key];
			if (moveIndex == undefined) {
				// 是一个新元素，添加进去
				parent.insertBefore(createElm(newStartVnode), oldStartVnode.el)
			} else {
				let moveVnode = oldChildren[moveIndex];
				oldChildren[moveIndex] = null; //直接删除可能会导致数组塌陷
				parent.insertBefore(moveVnode.el, oldStartVnode.el)
				// 对比当前这两个元素的属性和子元素
				patch(moveVnode, newStartVnode);

			}
			newStartVnode = newChildren[++newStartIndex];
		}
	}
	// 新节点增加的
	if (newStartIndex <= newEndIndex) {
		for (let i = newStartIndex; i <= newEndIndex; i++) {
			// /无论是往前插入还是往后插入，都可以通过insertBefore实现，这里的算法还是比较巧妙的
			let ele = newChildren[newEndIndex + 1] == null ? null : newChildren[newEndIndex + 1].el;
			if (ele) {
				parent.insertBefore(createElm(newChildren[i]), ele);
			}
			// parent.appendChild(createElm(newChildren[i]));
		}
	}
	// 删除多余的节点
	if (oldStartIndex <= oldEndIndex) {
		for (let i = oldStartIndex; i <= oldEndIndex; i++) {
			let child = oldChildren[i];
			if (child) {
				parent.removeChild(child.el);
			}
		}
	}

}

/**
 * 创建真是节点
 */
export function createElm(vnode) { //需要递归创建
	let { tag, children, data, key, text } = vnode;
	if (typeof tag === 'string') {
		// 元素
		// 虚拟节点与真实节点进行映射， diff 阶段元素复用
		vnode.el = document.createElement(tag);
		updateProperties(vnode);
		// 递归渲染子节点
		children.forEach(child => {
			// 子节点放入父节点
			vnode.el.appendChild(createElm(child))
		})
	} else {
		// 普通文本
		vnode.el = document.createTextNode(text);
	}
	return vnode.el;
}

/**
 * 更新元素属性
 * @param {*} vnode 
 */
function updateProperties(vnode, oldProps = {}) {
	let el = vnode.el;
	let newProps = vnode.data || {};
	// 获取老样式和新样式的差异
	let newStyle = newProps.style || {};
	let oldStyle = oldProps.style || {};
	// 删除了属性
	Object.keys(oldStyle).forEach(key => {
		if (!newStyle[key]) {
			el.style[key] = ''; //删除之前的样式
		}
	})
	// 新增了属性
	Object.keys(newStyle).forEach(key => {
		if (!newProps[key]) {
			el.removeAttribute(key);
		}
	})
	// 其他情况直接覆盖即可
	Object.keys(newProps).forEach(key => {
		if (key === 'style') {
			Object.keys(newProps.style).forEach(styleName => {
				el.style[styleName] = newProps.style[styleName];
			})
		} else {
			//  event slot 未处理
			el.setAttribute(key, newProps[key])
		}
	})
}